﻿using ZMQ;
using NLog;
using System;
using System.Text;
using Newtonsoft.Json;
using System.Threading;
using System.Configuration;
using System.ServiceProcess;
using AuctionServer.Domain;
using Newtonsoft.Json.Converters;
using System.Collections.Generic;

namespace AuctionServer
{
    public class AuctionService : ServiceBase
    {
        private Timer timer;
        private bool updating;
        private Thread workerThread;
        private readonly Logger logger;
        private static BidServiceStatus status;
        private readonly string serviceName;
        private const int UpdateInterval = 1000;
        private readonly string replyerEndpoint;
        private readonly string publisherEndpoint;
        private readonly JsonConverter converter;
        private readonly object statusLock = new object();
        private readonly object updateLock = new object();
        public AuctionService()
        {
            serviceName = ConfigurationManager.AppSettings["serviceName"] ?? "AuctionService";
            replyerEndpoint = ConfigurationManager.AppSettings["replyerEndpoint"] ?? "tcp://127.0.0.1:6000";
            publisherEndpoint = ConfigurationManager.AppSettings["publisherEndpoint"] ?? "tcp://127.0.0.1:5000";
            converter = new JavaScriptDateTimeConverter();
            logger = LogManager.GetCurrentClassLogger();
            status = BidServiceStatus.Closed;
            CanPauseAndContinue = false;
            ServiceName = serviceName;
            AutoLog = false;
        }

        protected override void OnStart(string[] args)
        {
            if ((workerThread == null) || ((workerThread.ThreadState & (ThreadState.Unstarted | ThreadState.Stopped)) != 0))
            {
                workerThread = new Thread(ZeroMq) { IsBackground = true, Priority = ThreadPriority.AboveNormal };
                workerThread.Start();
            }
            logger.Info("OnStart");
        }

        protected override void OnStop()
        {
            if ((workerThread != null) && (workerThread.IsAlive))
            {
                workerThread.Abort();
                workerThread = null;
            }
            logger.Info("OnStop");
            ExitCode = 0;
        }

        private void ZeroMq()
        {
            try
            {
                using (var context = new Context(1))
                using (Socket replyer = context.Socket(SocketType.REP), publisher = context.Socket(SocketType.PUB))
                {
                    replyer.Bind(replyerEndpoint);
                    publisher.Bind(publisherEndpoint);
                    while (true)
                    {
                        var data = replyer.Recv(Encoding.UTF8);
                        if (string.IsNullOrEmpty(data))
                        {
                            replyer.Send(data, Encoding.UTF8);
                        }
                        else
                        {
                            logger.Info(data);
                            var request = JsonConvert.DeserializeObject<BidRequest>(data);
                            if (request != null)
                            {
                                switch (request.Cmd)
                                {
                                    case "open":
                                        Open(publisher);
                                        replyer.Send(status.ToString(), Encoding.UTF8);
                                        break;
                                    case "close":
                                        Close();
                                        replyer.Send(status.ToString(), Encoding.UTF8);
                                        break;
                                    case "reset":
                                        Reset(publisher);
                                        replyer.Send(status.ToString(), Encoding.UTF8);
                                        break;
                                    case "get":
                                        //Close(replyer, publisher);
                                        break;
                                    case "bid":
                                        Bid(replyer, publisher, request.Param);
                                        break;
                                    default:
                                        replyer.Send(data, Encoding.UTF8);
                                        break;
                                }
                            }
                            else
                            {
                                replyer.Send(data, Encoding.UTF8);
                            }
                        }
                    }
                }
            }
            catch (ZMQ.Exception e)
            {
                logger.Error(e.Message);
            }
        }

        private static void Test(object state)
        {
            var publisher = state as Socket;
            if (publisher != null)
            {
                var data = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                publisher.Send(data, Encoding.UTF8);
            }
        }

        private void Open(Socket publisher)
        {
            if (status != BidServiceStatus.Open || status != BidServiceStatus.Opening)
            {
                lock (statusLock)
                {
                    if (status != BidServiceStatus.Open || status != BidServiceStatus.Opening)
                    {
                        status = BidServiceStatus.Opening;
                        Despatcher.Instance.Load();
                        timer = new Timer(Update, publisher, UpdateInterval, UpdateInterval);
                        status = BidServiceStatus.Open;
                    }
                }
            }
        }

        private void Close()
        {
            if (status == BidServiceStatus.Open || status == BidServiceStatus.Opening)
            {
                lock (statusLock)
                {
                    if (status == BidServiceStatus.Open || status == BidServiceStatus.Opening)
                    {
                        status = BidServiceStatus.Closing;
                        if (timer != null)
                        {
                            timer.Dispose();
                        }
                        Despatcher.Instance.Update();
                        status = BidServiceStatus.Closed;
                    }
                }
            }
            //var result = new SkResult<string>(false, "closed");
            //var data = JsonConvert.SerializeObject(result, converter);
            //publisher.Send(data, Encoding.UTF8);
        }

        private void Reset(Socket publisher)
        {
            lock (statusLock)
            {
                Close();
                Open(publisher);
            }
        }

        #region
        public void Bid(Socket replyer, Socket publisher, BidParam param)
        {
            var result = new SkResult<object>(false);
            if (status != BidServiceStatus.Open)
            {
                result.Message = "服务未开启";
            }
            else
            {
                var user = Despatcher.Instance.GetUser(param.UserId);
                var auction = Despatcher.Instance.GetAuction(param.AuctionId);
                var tmp = ValidateBid(user, auction, param.IpAddress);
                var o = tmp.Data;
                result = new SkResult<object>(tmp.Status, tmp.Message, new { o.AuctionId, o.UserId, o.NickName, o.CurrentPrice, o.CreatedIp });
            }
            var data = JsonConvert.SerializeObject(result);
            logger.Info(data);
            replyer.Send(data, Encoding.UTF8);
            if (result.Status)
            {
                publisher.Send(data, Encoding.UTF8);
            }
        }
        private static SkResult<Bidding> ValidateBid(UserInfo user, AuctionInfo auction, string ip)
        {
            var result = new SkResult<Bidding>(false);

            if (auction.Status == AuctionStatus.Dealt)
            {
                result.Message = "该拍品已成交";
            }
            else if (DateTime.Now < auction.OnlineTime || DateTime.Now < auction.StartDateTime)
            {
                result.Message = "不在竞拍时间内";

            }
            else if (user.Id == auction.CurrentUserId)
            {
                result.Message = "不可连续出价";
            }
            else if (user.AuctionMoney < auction.PerPrice)
            {
                result.Message = "余额不足";
            }
            else
            {
                return Despatcher.Instance.UpdateInfo(user, auction, ip);
            }
            return result;
        }
        #endregion
        private void Update(object state)
        {
            var publisher = state as Socket;
            if (publisher == null || updating) return;
            var result = new SkResult<IDictionary<long, bool>>(false);
            lock (updateLock)
            {
                if (!updating)
                {
                    updating = true;
                    var list = Despatcher.Instance.GetDealtOrStopAuction();
                    if (list.Count > 0)
                    {
                        result.Data = list;
                        result.Status = true;
                        result.Extra = "update";
                    }
                    updating = false;
                }
            }
            if (!result.Status) return;
            var data = JsonConvert.SerializeObject(result);
            publisher.Send(data, Encoding.UTF8);
        }
    }
}
